#ifndef SHARED_ARRAY_H
#define SHARED_ARRAY_H

#include <cstdlib>
#include <cassert>

// Array with shared ownership (with counting references).
template <typename T>
class SharedArray {
	public:
		SharedArray():
				_data(0), _size(0), _references(new int(1)) {}

		SharedArray(int size):
				_data(size > 0 ? new T[size] : 0), _size(size), _references(new int(1)) {}

		SharedArray(const SharedArray<T>& rhs):
				_data(rhs._data), _size(rhs._size), _references(rhs._references) {
			increaseReferences();
		}

		~SharedArray() {
			decreaseReferences();
		}

		SharedArray<T>& operator=(const SharedArray<T>& rhs) {
			if (_references == rhs._references)
				return *this;

			decreaseReferences();

			_data = rhs._data;
			_size = rhs._size;
			_references = rhs._references;
			increaseReferences();

			return *this;
		}

		SharedArray<T> deepCopy() const {
			SharedArray<T> array;
			array._data = new T[_size];
			memcpy(array._data, _data, sizeof(T) * _size);
			array._size = _size;
			assert(*array._references == 1);

			return array;
		}

		void swap(SharedArray<T>& rhs) {
			T* data = _data;
			int size = _size;
			int* references = _references;

			_data = rhs._data;
			_size = rhs._size;
			_references = rhs._references;

			rhs._data = data;
			rhs._size = size;
			rhs._references = references;
		}

		const T& operator[](int i) const {
			assert(i < _size);
			return _data[i];
		}

		T& operator[](int i) {
			assert(i < _size);
			return _data[i];
		}

		bool operator==(const SharedArray<T>& rhs) const {
			return _references == rhs._references;
		}

		bool operator!=(const SharedArray<T>& rhs) const {
			return !(*this == rhs);
		}

		int size() const {
			return _size;
		}

		int references() const {
			return *_references;
		}

		const T* data() const {
			return _data;
		}

		T* data() {
			return _data;
		}

		// shrinks array without manipulating data
		// remaining space is lost, but still deleted in destructor
		void shrink(int size) {
			assert(size <= _size);
			_size = size;
		}

	private:
		void decreaseReferences() {
			--*_references;

			if (*_references == 0) {
				delete[] _data;
				delete _references;
			}
		}

		void increaseReferences() {
			++*_references;
		}

		SharedArray(T* data, int size, int* references):
				_data(data),
				_size(size),
				_references(references) {
			increaseReferences();
		}

		T* _data;
		int _size;
		int* _references;

		friend class Column;
};

#endif
